﻿#include <iostream>
#include <fstream>
#include <QCoreApplication>
#include <QTextCodec>
#include <QCommandLineParser>
#include <QDir>

#include "antlr4-runtime.h"
#include "PinLexer.h"
#include "PinParser.h"
#include "ANTLRInputStream.h"
#include "PinBaseListener.h"
#include "ExprVisitor.h"
#include "BlockListener.h"
#include "global.h"
#include "PinErrListener.h"
#include "cppapi.h"

#ifdef WIN32
#include <windows.h>
#endif

#define version_str "0.7.1 (2025.04.27)"


#pragma execution_character_set("utf-8")

using namespace antlr4;
using namespace std;



int ExceptStep = 0;

//是否有异常。
bool s_isExcep = false;

QByteArray s_srcUtf8Bytes;

//主函数是否有参数
//int s_mainFunPara = -1; //-1 未知 0 没有 1有 2已经调用，不需要二次调用

CommonTokenStream* tokensListen = nullptr;

//加入内部全局定义代码，主要预定义一些字符串
void addIntelCodeText(std::string & srcCode,int type = 0)
{
    antlr4::ANTLRInputStream bodyStream(srcCode);
    PinLexer lexer(&bodyStream);
    CommonTokenStream tokens(&lexer);
    PinParser parser(&tokens);

    PinErrListener errListener;
    errListener.tokens = &tokens;

    parser.removeErrorListeners();
    parser.addErrorListener(&errListener);

    tree::ParseTree* tree = nullptr;
    if(type == 0)
    {
        tree = parser.defglobal();
    }
    ExprVisitor expr;
    expr.visit(tree);
}

//加入pin语言内部定义的结构体。
//XLWorksheet 操作一张sheet表的句柄
std::string pinGlobalStructDesc = R"(quanju_dingyi {
               jiegou XLWorksheet{
               excel obj;
               zfc sheetName;
               };


               jiegou RegExp{
                    string pattern;
               };

               jiegou RegMatch{
               };

               jiegou RegMatchIter{
               };
})";

std::atomic<bool> shouldExit(false);
#define INIT_EXIT_CODE -9999
int s_exit_code = INIT_EXIT_CODE;
int main(int argc, char *argv[])
{
    shouldExit = false;
    resetGlboalVar();

#ifdef PIN_MEM_REF
    newTimes = 0;
    freeTimes = 0;
#endif

#ifdef WIN32
    SetConsoleOutputCP(CP_UTF8);
#endif 

#ifdef PIN_DEBUG
   #define ANTLR4_DEBUG 1
#endif

    QCoreApplication app(argc, argv);
    QCoreApplication::setApplicationName("Pinlang");
    QCoreApplication::setApplicationVersion(version_str);


    //QCommandLineParser parser;
    //parser.setApplicationDescription(u8"示例程序");
    //parser.addHelpOption();
    //parser.addVersionOption();

    // 添加位置参数：第一个参数为文件路径（无选项标志）
    /*parser.addPositionalArgument(
        "file",                  // 参数名称（仅用于帮助文档）
        QString(u8"要运行的pinlang 脚本路径。") // 描述
        //QString("[file]") // 语法占位符（可选）
        );
        */
    //parser.process(app);
     QStringList arguments = QCoreApplication::arguments();
    //parser.parse(arguments);

    // 获取位置参数列表
    //const QStringList positionalArgs = parser.positionalArguments();
    QString info = QString(u8"\n使用方法：pinlang file [options]\n\n版本信息：pinlang version %1").arg(version_str);

    //必须是 脚本文件 参数的方式。
    if (arguments.size() < 2) {

        std::cout << info.toStdString() << std::endl;

#ifdef _DEBUG
        arguments.append("test.pin");
        //return 1;
#else
        return 1;
#endif
    }

    //第一个参数只能是文件名称
    QString filePath = arguments.at(1);

    QFileInfo fi(filePath);

    //第一个参数必须是文件。
    if (!fi.isFile() || !fi.exists())
    {

        if (filePath == "-v" || filePath == "--version")
        {
            info = QString(u8"版本信息：pinlang version %1").arg(version_str);
        }
        else
        {
            QString err = QString(u8"不存在的文件 %1").arg(filePath);

            std::cout << err.toStdString() << std::endl;
        }
        std::cout << info.toStdString() << std::endl;
        return 1;
    }
    if (fi.isRelative())
    {
        QString absDir = QDir::currentPath();
        //获取绝对路径
        filePath = QString("%1/%2").arg(absDir).arg(filePath);
    }

    //如果有后续的参数，则把参数放入一个全局列表中,后续让man函数可以去解析。

    //把gbk utf8 和可以识别的编码，转换为utf8编码。
    {
        std::ifstream ifs(filePath.toStdString());  // 打开文件 example.txt
        if (!ifs.is_open()) {  // 检查文件是否成功打开
            //QString programPath = QCoreApplication::applicationDirPath();

            QString err = QString(u8"无法打开文件 %1 请检查路径是否存在或是否有可读权限！").arg(filePath);
            std::cerr << err.toStdString() << std::endl;
            return 1;
        }
        std::stringstream buffer;
        buffer << ifs.rdbuf();

        ifs.close();

        if (!buffer) {
            std::cerr << u8"读取源代码文件错误！" << std::endl;
            return 1;
        }

        //检查源码是不是utf8格式。
        QByteArray bytes = QByteArray::fromStdString(buffer.str());

        checkCodeTextEncode((const uchar*)bytes.constData(), bytes.size(), s_srcUtf8Bytes);

    }

       ExceptStep = 0;

       ANTLRInputStream input(s_srcUtf8Bytes.data(), s_srcUtf8Bytes.size());

       PinLexer lexer(&input);

       tokensListen = new CommonTokenStream(&lexer);
#ifdef PIN_MEM_REF
       newTime();
#endif
       BlockListener* blockLsr = new BlockListener();
       blockLsr->tokens = tokensListen;

#ifdef PIN_MEM_REF
            newTime();
#endif
        s_isExcep = false;
       PinParser* parserListen = nullptr;

       try{
            initVarGlobalSymTable();
           //从函数定义开始扫描，把所有函数的定义扫描出来。
            parserListen= new PinParser (tokensListen);
#ifdef PIN_MEM_REF
            newTime();
#endif
           PinErrListener errListener;
           lexer.removeErrorListeners();
           parserListen->removeErrorListeners();
           lexer.addErrorListener(&errListener);
           parserListen->addErrorListener(&errListener);

           errListener.tokens = tokensListen;
           errListener.pSrcCode = &s_srcUtf8Bytes;

           //还没有进入监听器就出错了。
           ExceptStep = 1;

           tokensListen->fill();

           tree::ParseTree* tree1 = parserListen->program();
           antlr4::tree::ParseTreeWalker* walker = new antlr4::tree::ParseTreeWalker();
           walker->walk(blockLsr, tree1);

           ExceptStep = 2;
#ifdef PIN_MEM_REF
            newTime();
#endif

            delete walker;
            delete blockLsr;
#ifdef PIN_MEM_REF
            freeTimes+=2;
#endif
       }
       catch (const antlr4::RecognitionException& e) {
           std::cerr << u8"捕获语法识别异常: " << e.what() << std::endl;
           s_isExcep = true;
       } catch (const std::runtime_error& e) {
           std::cerr << u8"脚本指令中存在语法错误： \n" << e.what() << std::endl;
           s_isExcep = true;
       } catch (const std::exception& e) {
           std::cerr << u8"捕获通用异常信息: " << e.what() << std::endl;
           s_isExcep = true;
       }catch (...) {
           std::cerr << u8"捕获异常" << std::endl;
           s_isExcep = true;
       }
            if(s_isExcep)
            {
                destoryPinFunSysTable();
                destoryVarGlobalSymTable();
#ifdef WIN32
                HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
                // 恢复默认颜色（前景色为白色，背景色为黑色）
                SetConsoleTextAttribute(hConsole, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
#endif
                return 0;
            }
#ifdef PIN_DEBUG
            std::cout << u8"--1内存new 次数" << newTimes << u8"内存free 次数" << freeTimes << std::endl;
#endif
       /*
       //坑，如果后续要继续使用同一个tokens，必须要reset一下，否则导致后续访问器会失败。
       tokens.reset();
       PinParser parser(&tokens);

       //
       tree::ParseTree* tree = parser.program();

       ExprVisitor expr;
       expr.visit(tree);
*/

       //先访问全局区域
        //初始化一下全局符号表
       // initVarGlobalSymTable();

       pushStatus(INTO_GLOBAL);

            //初始化结构名称和底部id的对应表。
       initStructDescIdMap();
       initListMapRefNum();
       addIntelCodeText(pinGlobalStructDesc);

       //global也行
       const std::string globalFun[2] = {"quanju_dingyi","global"};

       for(int i=0; i < 2; ++i)
       {
           FUN_SYM_INFO* pGlobal = findFunBlock(globalFun[i]);
       if(pGlobal != nullptr)
       {
           bool isExcep = false;
       try{
           //tree::ParseTree* tree = parser.defglobal();
           ExprVisitor expr;
           //expr.visit(tree);

           expr.visitDefglobal(dynamic_cast<PinParser::DefglobalContext*>(pGlobal->pBlock));
       }
       catch (const antlr4::RecognitionException& e) {
           std::cerr << u8"捕获语法识别异常: " << e.what() << std::endl;
           isExcep = true;
       } catch (const std::runtime_error& e) {
           std::cerr << u8"脚本指令中存在语法错误： \n" << e.what() << std::endl;
           isExcep = true;
       } catch (const std::exception& e) {
           std::cerr << u8"捕获通用异常信息: " << e.what() << std::endl;
           isExcep = true;
       }catch (...) {
           std::cerr << u8"捕获异常" << std::endl;
           isExcep = true;
       }
       if(isExcep)
       {
               destoryPinFunSysTable();

               destoryStructDescIdMap();
               destoryVarGlobalSymTable();
               destoryListMapRefNum();
#ifdef WIN32
               HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
               // 恢复默认颜色（前景色为白色，背景色为黑色）
               SetConsoleTextAttribute(hConsole, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
#endif
               return 0;
       }

               //找到一个就结束
               break;
       }
       }
       popStatus();

#ifdef PIN_DEBUG
       //std::cout << u8"--1内存new 次数" << newTimes << u8"内存free 次数" << freeTimes << std::endl;
#endif
       bool isCallMain = false;

       FUN_SYM_INFO* pFunInfo = findFunBlock("hs_rukou");
       if (pFunInfo == nullptr) {

           //main函数也行
           pFunInfo = findFunBlock("main");

           if(pFunInfo == nullptr)
           {
               std::cout << u8"没有发现入口函数hs_rukou 或 main, 程序调用失败,请检查！ " << std::endl;
           destoryPinFunSysTable();

           destoryStructDescIdMap();
           destoryVarGlobalSymTable();
            destoryListMapRefNum();

           return 0;
       }
           isCallMain= true;
       }
       //发现了hs_rukou 或 main
       {
           initListMapRefNum();
           initFunCallStack();
           initCppFunTab();

           //获取入口的代码后，开始解析执行
		   #ifdef PIN_DEBUG
        std::string* pBlock = pFunInfo->funBlock;
           std::cout << u8"将执行函数体代码" << *pBlock << std::endl;
		   #endif

           std::string callMain = isCallMain? "main();" : "hs_rukou();";

           int isNeedRetValue = -1;

           //如果还有其他参数，则作为脚本的程序参数输入
           //if (arguments.size() > 2)
           {
               //s_mainFunPara = 0;
               SymNode* pMainCallFun = nullptr;

               //如果入口函数有定义参数，只能是lb name的方式
               std::vector<std::string>* paraList = pFunInfo->paraList;
               if (paraList != nullptr)
               {
                   //
                  if(paraList->size() == 2 && (paraList->at(0).size() > 0) && paraList->at(1) == "lb")
                   {
                       //把外部输入的参数，做成一个列表参数，后续要传递一下才行。
                       SymNode* programPara = new SymNode();
#ifdef PIN_MEM_REF
                       newTime();
#endif
                       programPara->type = EXPR_LIST;
                       programPara->name = paraList->at(0);

                       std::vector<ExprValue*>* pData = nullptr;

                       if (arguments.size() > 2)
                       {
                           pData = new std::vector<ExprValue*>(arguments.size() - 2);
                            #ifdef PIN_MEM_REF
                                newTime();
                            #endif
                       }
                       else
                       {
                           pData = new std::vector<ExprValue*>();
#ifdef PIN_MEM_REF
                           newTime();
#endif
                       }

                       programPara->value.vpoint = pData;
                       addListMapRefNum(pData);
                       //加入到全局变量中去

                       for (int i = 2, s = arguments.size(); i < s; ++i)
                       {
                           QByteArray b = arguments.at(i).toUtf8();

                           ExprValue* pExpr = new ExprValue();
                           pExpr->type = EXPR_STRING;
                           pExpr->value.vpoint = new QByteArray(std::move(b));
#ifdef PIN_MEM_REF
                           newTime(2);
#endif
                           pData->operator[](i - 2) = pExpr;
                       }

                       //表明主函数有参数，后续需要调用。
                       //s_mainFunPara = 1;

                       //封装一个根函数，去调用入口函数
                       pMainCallFun = new SymNode;

#ifdef PIN_MEM_REF
                       newTime();
#endif
                       pMainCallFun->type = FUN_SYM;
                       pMainCallFun->name = "cmain";

                       getPinSymTable()->append(pMainCallFun);
                       //现在是在最外层的栈上面，还没有进入主函数的栈。这个参数一定是第一个。
                       getPinSymTable()->append(programPara);

                       callMain = (isCallMain?"main(" : "hs_rukou(") + paraList->at(0) + ");";
                   }
                  else
                  {

                      QString info = QString(u8"错误：行号 %1 函数 %2 形式不匹配，参数错误！只能是 hs_rukou() 或 hs_rukou(lb canshu) 或main() main(lb canshu) 四种形式！")
                          .arg(pFunInfo->funNameLineNum).arg(QStr(pFunInfo->funDec));
                      std::cout << info.toStdString() << std::endl;

                      return 1;
                  }
               }

                //是否有返回值
               std::vector<std::string>* pRetValue = pFunInfo->retList;
               if (pRetValue != nullptr && pRetValue->size() == 1 && pRetValue->at(0) == "zs")
               {
                   //有合法的返回值。
                   isNeedRetValue = 1;

                   if (pMainCallFun == nullptr)
                   {
                       //封装一个根函数，去调用入口函数
                       pMainCallFun = new SymNode;

#ifdef PIN_MEM_REF
                       newTime();
#endif
                       pMainCallFun->type = FUN_SYM;
                       pMainCallFun->name = "cmain";
                       getPinSymTable()->append(pMainCallFun);


                   }
                   pMainCallFun->value.vint = NEED_FUN_RET_VALUE;
                   callMain = "fanhui " + callMain;

                   //cmain 函数要关心返回值，然后续执行rs_rukou真实返回值，否则后续崩溃。
                   std::vector<std::string> paraList;
                   std::vector<std::string> retList(1);
                   std::string block = "{}";
                   retList[0] = "zs";

                   FUN_SYM_INFO* pNewSym = addFunBlock(pMainCallFun->name, block, paraList, retList);
                   pNewSym->funNameLineNum = pFunInfo->funNameLineNum;
                   pNewSym->blockLineNum = pFunInfo->funNameLineNum;


               }
               else if(pRetValue != nullptr)
               {
                   QString info = QString(u8"错误：行号 %1 函数 %2 返回值不合法！hs_rukou() 最多只能返回1个整数!")
                       .arg(pFunInfo->funNameLineNum).arg(QStr(pFunInfo->funDec));
                   std::cout << info.toStdString() << std::endl;
                   return 1;
               }
           }

            //这里是在手动生成一个call hs_rukou的方式，进而调用入口函数，让程序驱动起来，看起来不太优雅
           antlr4::ANTLRInputStream bodyStream(callMain);

           PinLexer lexer(&bodyStream);

           CommonTokenStream tokens(&lexer);

           PinParser parser(&tokens);

           PinErrListener errListener;
           errListener.tokens = &tokens;

           parser.removeErrorListeners();
           parser.addErrorListener(&errListener);

           s_isExcep = false;
           try{
           tree::ParseTree* tree = parser.callFun();

           ExprVisitor expr;
           expr.visit(tree);
           }
           catch (const antlr4::RecognitionException& e) {
               std::cerr << u8"捕获语法识别异常: " << e.what() << std::endl;
               s_isExcep = true;
           } catch (const std::runtime_error& e) {

               QString str(e.what());
               if (shouldExit)
               {
                   //合法中断。
                   std::cerr << e.what() << std::endl;
               }
               else
               {
                   //说明不是通过pin.exie退出
                   if (s_exit_code == INIT_EXIT_CODE)
                   {
                        std::cerr << u8"\n脚本指令中存在语法错误： \n" << e.what() << std::endl;
                   }
                   else
                   {
                       std::cout << e.what() << std::endl;
                   }
                   s_isExcep = true;
               }
           } catch (const std::exception& e) {
               std::cerr << u8"捕获通用异常信息: " << e.what() << std::endl;
               s_isExcep = true;
           }catch (...) {
               std::cerr << u8"捕获异常" << std::endl;
               s_isExcep = true;
           }

           if(s_isExcep)
           {
               printStackInfo();
           }
           else
           {
               //没有异常，主入口有返回值，则接受
               if (isNeedRetValue == 1)
               {
                   QList<SymNode*>* retList = getcurFunReturnValue();

                   //有返回值。
                   if (retList != nullptr && retList->size() == 1)
                   {
                       SymNode* pRet = retList->at(0);
                       isNeedRetValue = pRet->value.vint;

                       pRet->destory();
                       delete pRet;

                       delete retList;
#ifdef PIN_MEM_REF
                       freeTimes += 2;
#endif
                       pRet = nullptr;

                   }
                   else
                   {
                       //要返回值，但是没有给，默认返回0拉倒。
                       isNeedRetValue = 0;
                   }
               }
           }

           if(tokensListen!=nullptr)
           {
               delete tokensListen;
               tokensListen = nullptr;
#ifdef PIN_MEM_REF
               freeTimes ++;
#endif
           }

           if(parserListen != nullptr)
           {
               delete parserListen;
               parserListen = nullptr;
#ifdef PIN_MEM_REF
               freeTimes ++;
#endif
           }

           //回收全局内存。要注意顺序，和初始化反着来。
            //有异常不走，避免崩溃
           //必须在destoryListMapRefNum前面，否则里面会二次调用destoryListMapRefNum里面的东东而崩溃
           //if (!isExcep)
           {
               destoryPinSymTable();
               destoryCppFunTab();
           }

           destoryFunCallStack();


           destoryPinRunStatus();

           destoryVarGlobalSymTable();


           destoryListMapRefNum();//必须放在destoryVarGlobalSymTable的后面，因为destoryVarGlobalSymTable里面要使用
           destoryPinFunSysTable();

           destoryStructDescIdMap();

           s_srcUtf8Bytes.clear();

           //遇到异常退出
           if (s_isExcep)
           {
               //包含pin.exit退出。
               if (s_exit_code != INIT_EXIT_CODE)
               {
                   return s_exit_code;
               }
               return -1;
           }
           else
           {
            #ifdef PIN_MEM_REF
                           std::cout << u8"\n程序执行完毕。内存new 次数" << newTimes << u8" 内存free 次数" << freeTimes << std::endl;
            #else
                            std::cout << u8"\n程序执行完毕。"<< std::endl;
            #endif

           }
           return isNeedRetValue;
       }


    return 0;
}

